package common

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"sort"
	"time"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/process"
)

type files struct {
	FileName string
	Time     time.Time
}

var Pal *PalServer

type PalServer struct {
	token string
	run   bool
}

func NewPal() *PalServer {
	user := GetInfo()
	t, _ := ReleaseToken(*user)
	return &PalServer{
		token: t,
		run:   false,
	}
}

func (p *PalServer) Update() {
	config := GetInfo()
	p.Kill()
	cmd := exec.Command(fmt.Sprintf("%s/steamcmd", config.CMDPath), "+login", "anonymous", "+app_update", "2394010", "validate", "+quit")
	cmd.Run()
	p.RunSafe()
}

func (p *PalServer) GetMem() float64 {
	memory, err := mem.VirtualMemory()
	if err != nil {
		log.Printf("Get mem error: %s", err.Error())
		return 0
	}
	return memory.UsedPercent
}

func (p *PalServer) GetCpu() float64 {
	cp, err := cpu.Percent(time.Second, false)
	if err != nil {
		log.Printf("Get mem error: %s", err.Error())
		return 0
	}
	return cp[0]
}

func (p *PalServer) KillProcess(killname string) {
	pids, err := process.Pids()
	if err != nil {
		log.Printf("Find process error: %s", err.Error())
		return
	}
	for _, v := range pids {
		p, err := process.NewProcess(v)
		if err != nil {
			log.Printf("Open process error: %s", err.Error())
			continue
		}
		name, err := p.Name()
		if err != nil {
			log.Printf("Get processname error: %s", err.Error())
			continue
		}
		if name == killname {
			log.Printf("Start kill PID: %d Name: %s", p.Pid, name)
			err = p.Kill()
			if err != nil {
				log.Printf("Kill process error: %s", err.Error())
			}
		}
	}
}

func (p *PalServer) Kill() {
	p.KillProcess("PalServer-Linux-Test")
	p.KillProcess("PalServer.sh")
	p.run = false
}

func (p *PalServer) ShutDown() {
	info := GetInfo()
	go NewRcon().ShutDown(info.CloseDuration, info.CloseMsg)
	NewRcon().Save()
	p.run = false
}

func (p *PalServer) StartProcess() {
	config := GetInfo()
	cmd := exec.Command(fmt.Sprintf("%s/PalServer.sh", config.PalPath))
	go func() {
		err := cmd.Run()
		if err != nil {
			log.Printf("Start PalServer Error: %s", err.Error())
		}
		log.Println("Start PalServer Successful")
	}()
}

func (p *PalServer) SortFiles(fs []string) ([]files, bool) {
	f := make([]files, 0)
	for _, v := range fs {
		t, err := time.Parse("2006年01月02日15时04分05秒", v)
		if err != nil {
			continue
		}
		tmp := files{
			FileName: v,
			Time:     t,
		}
		f = append(f, tmp)
	}
	lessFunc := func(i int, j int) bool {
		return f[i].Time.Before(f[j].Time)
	}
	if len(fs) < 10 {
		return f, false
	}
	sort.SliceStable(fs, lessFunc)
	return f, true
}
func (p *PalServer) InitSave() string {
	config := GetInfo()
	bkf := fmt.Sprintf("%s/bak", config.PalPath)
	files, _ := os.ReadDir(bkf)
	fs := make([]string, 0)
	for _, v := range files {
		if v.Type().IsDir() {
			fs = append(fs, v.Name())
		}
	}
	f, ok := p.SortFiles(fs)
	if ok {
		err := os.RemoveAll(fmt.Sprintf("%s/%s", bkf, f[0].FileName))
		if err != nil {
			log.Printf("remove bak file err: %s", err.Error())
		}
	}
	filename := time.Now().Format(fmt.Sprintf("%s/2006年01月02日15时04分05秒", bkf))
	os.Mkdir(filename, 0777)

	return filename
}

func (p *PalServer) BakSave() {
	config := GetInfo()
	p.exist()
	for {
		path := p.InitSave()
		cmd := exec.Command("cp", "-rf", fmt.Sprintf("%s/Pal/Saved/SaveGames", config.PalPath), fmt.Sprint(path))
		cmd2 := exec.Command("cp", "-rf", fmt.Sprintf("%s/Pal/Saved/Config", config.PalPath), fmt.Sprint(path))
		go func() {
			err := cmd.Run()
			if err != nil {
				log.Printf("Save SaveGames Error: %s", err.Error())
				return
			}
			log.Println("Save SaveGames Successful")
		}()
		go func() {
			err := cmd2.Run()
			if err != nil {
				log.Printf("Save Config Error: %s", err.Error())
				return
			}
			log.Println("Save Config Successful")
		}()

		time.Sleep(time.Minute * time.Duration(config.BakSave))
	}
}

func (p *PalServer) exist() {
	config := GetInfo()
	_, err := os.Stat(fmt.Sprintf("%s/bak", config.PalPath))
	if os.IsNotExist(err) {
		err = os.Mkdir(fmt.Sprintf("%s/bak", config.PalPath), 0777)
		if err != nil {
			log.Printf("Create bak file error: %s", err.Error())
		}
	}
}

func (p *PalServer) RunSafe() {
	p.ShutDown()
	info := GetInfo()
	time.Sleep(time.Duration(info.CloseDuration+3) * time.Second)
	p.StartProcess()
	p.run = true
	var mem float64
	for p.run {
		mem = p.GetMem()
		log.Printf("VirtualMemory: %f%%", mem)
		if mem >= float64(info.Mem) {
			go p.RunSafe()
			break
		}
		time.Sleep(time.Minute * time.Duration(info.Duration))
	}
}

func (p *PalServer) Running() bool {
	pids, err := process.Pids()
	if err != nil {
		log.Printf("Find process error: %s", err.Error())
		return false
	}
	for _, v := range pids {
		p, err := process.NewProcess(v)
		if err != nil {
			log.Printf("Open process error: %s", err.Error())
			continue
		}
		name, err := p.Name()
		if err != nil {
			log.Printf("Get processname error: %s", err.Error())
			continue
		}
		if name == "PalServer.sh" {
			return true
		}
	}
	return false
}

func (p *PalServer) Token() string {
	return p.token
}

func (p *PalServer) DelBak(name string) error {
	info := GetInfo()
	//防止乱删
	_, err := time.Parse("2006年01月02日15时04分05秒", name)

	if err != nil {
		return fmt.Errorf("请勿乱删其他文件")
	}

	err = os.RemoveAll(fmt.Sprintf("%s/bak/%s", info.PalPath, name))

	if err != nil {
		return fmt.Errorf("删除失败: %s", err.Error())
	}

	return nil
}

func (p *PalServer) GetBaks() []string {
	config := GetInfo()
	bkf := fmt.Sprintf("%s/bak", config.PalPath)
	files, _ := os.ReadDir(bkf)
	fs := make([]string, 0)
	for _, v := range files {
		if v.Type().IsDir() {
			fs = append(fs, v.Name())
		}
	}
	return fs
}
